<script setup lang="ts">
import { ref, reactive, defineAsyncComponent, onUnmounted } from 'vue';
import { storeToRefs } from 'pinia';
import { io } from 'socket.io-client';
import useUserInfo from '@/stores/userInfo';
import { handleMessage } from '@/utils/globalEvent';
import type { ISocketUserParams, ISocketUser, ISocketMessage } from './type';
import type { IUser } from './cpn/userList/type';
import type { IPrivateSend } from './cpn/privateChat/type';

// 在线用户
const userList = defineAsyncComponent(() => import('./cpn/userList/index.vue'));
// 群聊名称
const groupChat = defineAsyncComponent(() => import('./cpn/groupChat/index.vue'));
// 群聊内容
const chatMessage = defineAsyncComponent(() => import('./cpn/message/index.vue'));
// 聊天框
const chatSend = defineAsyncComponent(() => import('./cpn/send/index.vue'));
// 私聊列表
const privateChat = defineAsyncComponent(() => import('./cpn/privateChat/index.vue'));

const stores = useUserInfo();
const { userInfo } = storeToRefs(stores);
// 私聊列表ref
const privateChatRef = ref();

// 创建 socket 实例
const socket = io(import.meta.env.VITE_SOCKET_API);
// 与服务端创建链接 携带信息
const socketParams: ISocketUserParams = {
	name: userInfo.value!.name,
	avatar: userInfo.value!.avatarUrl,
};

// 聊天室在线列表
const socketUserList = ref<ISocketUser[]>([]);
// 保存登陆人信息
const socketUser = ref<ISocketUser>();
// 聊天信息列表
const socketMessageList: ISocketMessage[] = reactive([]);
// 谁发送了私聊消息
const privateUser = ref<IUser[]>([]);
// 私聊信息列表
const privateMessage: any = reactive({});
// 展示私聊列表
const showPrivateMessage = ref([]);

// 发送加入事件
const handleJoin = (user: ISocketUserParams) => {
	socket.emit('join', user);
};
handleJoin(socketParams);
// 监听加入成功的事件
socket.on('joined', (e: ISocketUser) => {
	socketUser.value = e;
});
// 监听加入群聊全体广播
socket.on('welcome', (name, userList) => {
	handleMessage(`欢迎${name.name}加入聊天室`);
	socketUserList.value = userList;
});
// 监听退出群聊全体广播
socket.on('quit', id => {
	const userIndex = socketUserList.value.findIndex(item => item.id === id);
	if (userIndex === -1) return;
	handleMessage(`${socketUserList.value[userIndex].name}退出群聊`);
	socketUserList.value.splice(userIndex, 1);
});

// 群聊信息发送
const handleSend = (msg: string) => {
	const params = { ...socketUser.value, msg };
	socket.emit('send', params);
};
// 接受服务端群聊消息
socket.on('message', (e: ISocketMessage) => {
	socketMessageList.push(e);
});

// 点击用户
const clickUser = (user: IUser) => {
	// 点击的要是自己 则跳出
	if (user.id === socketUser.value?.id) return;
	// 建立当前私聊人聊天信息数组
	const hasKey = Object.keys(privateMessage).indexOf(user.id);
	if (hasKey === -1) privateMessage[user.id] = [];
	showPrivateMessage.value = [] = privateMessage[user.id];
	// 打开弹窗
	privateChatRef.value.setIsDrawer(true);
	// 设置接收与发送人
	privateChatRef.value.setUser(user, socketUser.value);
	// 发送消息列表存在某人
	privateUser.value = privateUser.value.filter(item => item.id !== user.id);
};
// 私聊信息发送
const privateSend = (params: object) => {
	socket.emit('privateSend', params);
};
// 接收服务端私聊发送人信息
socket.on('private-user', (e: IPrivateSend) => {
	privateMessage[e.acceptId].push(e);
});
// 接收服务端私聊信息
socket.on('private-message', (e: IPrivateSend) => {
	const { isDrawer, acceptUser } = privateChatRef.value;
	// 是否和该用户私聊过
	const hasKey = Object.keys(privateMessage).indexOf(e.id);
	// 若没私聊过 给该用户创建信息数组
	if (hasKey === -1) privateMessage[e.id] = [];
	// 把私聊信息推进数组
	privateMessage[e.id].push(e);
	// 提示谁来消息
	const { id, avatar, name } = e;
	// 若私聊窗口打开 且 正和发送人相同
	if (isDrawer && acceptUser.id === id) return;
	// 是否已经提示该用户来消息
	const privateUserIndex = privateUser.value.some(item => item.id === id);
	// 没有提示用户 给予提醒
	if (!privateUserIndex) privateUser.value.push({ id, avatar, name });
});

// 组件卸载完成
onUnmounted(() => {
	socket.disconnect();
});
</script>

<template>
	<div class="chat">
		<privateChat ref="privateChatRef" :private-message="showPrivateMessage" @handlePrivateSend="privateSend" />
		<div class="chat-left">
			<userList :user-list="socketUserList" @click-user="clickUser" />
		</div>
		<div class="chat-right">
			<groupChat :user-list="socketUserList" :private-user-list="privateUser" @click-user="clickUser" />
			<chatMessage :message-list="socketMessageList" />
			<chatSend @handle-send="handleSend" />
		</div>
	</div>
</template>

<style lang="less" scoped>
@import url('./index.less');
</style>
